package com.example.net.oss;

import android.content.Context;
import android.util.Log;

import com.alibaba.sdk.android.oss.ClientConfiguration;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.OSS;
import com.alibaba.sdk.android.oss.OSSClient;
import com.alibaba.sdk.android.oss.ServiceException;
import com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider;
import com.alibaba.sdk.android.oss.exception.InconsistentException;
import com.alibaba.sdk.android.oss.model.GetObjectRequest;
import com.alibaba.sdk.android.oss.model.GetObjectResult;
import com.alibaba.sdk.android.oss.model.OSSRequest;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;

/**
 * Created by Long on 2019/6/25.
 */

public class OSSUploader {
    private static final String TAG = "OSSUploader";
    public static final int SUCCESS = 0;
    public static final int CLIENT_ERROR = -1;
    public static final int SERVER_ERROR = -2;
    private OSS mOSS = null;
    private OSSConfig mConfig = null;

    public OSSUploader(Context context) {
        mConfig = OSSConfig.create();
        Log.i(TAG, "OSSUploader: " + mConfig.toString());
        mOSS = createOSS(context);
    }

    /**
     * 流式下载。即直接获取字节流
     *
     * @param objectKey 指定文件要下载的文件路径，
     * @return
     */
    public int streamDownload(final String objectKey, StreamDownLoadCallback callback) {
        if (callback == null) {
            throw new NullPointerException("the StreamDownLoadCallback is null!");
        }
        if (mOSS != null) {
            Log.i(TAG, "threadId = " + Thread.currentThread().getId() + " stream download objectKey " + objectKey);
            //构造下载请求
            GetObjectRequest get = new GetObjectRequest(mConfig.getBuckName(), objectKey);
            //开启crc校验。保证下载的文件的数据完整性
            get.setCRC64(OSSRequest.CRC64Config.YES);//getObjectContent返回的InputStream是CheckCRC64DownloadInputStream,crc校验是在它的read函数中处理的
            /**
             * OSS通过在Get请求的Header中添加Accept-Encoding为gzip，对常见网页静态文件（HTML、Javascript、XML、json）内容进行gzip压缩，但必须满足以下条件：
             * 文件大小大于或者等于 1KB。
             * Conetnt-Type必须是 text/cache-manifest、text/xml、text/plain、text/css、application/javascript、application/x-javascript、application/rss+xml、application/json、text/json 中的一个。
             * 对静态文件，GET请求默认是会将Accept-Encoding设置为gzip的
             */
            HashMap<String, String> map = new HashMap<>();
            map.put("Accept-Encoding", "");
            get.setRequestHeaders(map);
            InputStream is = null;
            GetObjectResult result = null;
            try {
                // 同步执行下载请求，返回结果
                result = mOSS.getObject(get);
                is = result.getObjectContent();
                callback.process(is);
            } catch (InconsistentException e) {
                e.printStackTrace();
                Log.i(TAG, "crc error -- > " + e.getMessage());
                return CLIENT_ERROR;
            } catch (ClientException e) {
                e.printStackTrace();
                Log.i(TAG, "ClientException-->" + e.getMessage());
                return CLIENT_ERROR;
            } catch (ServiceException e) {
                // 服务异常
                Log.i(TAG, "RequestId " + e.getRequestId());
                Log.i(TAG, "ErrorCode " + e.getErrorCode());
                Log.i(TAG, "HostId " + e.getHostId());
                Log.i(TAG, "RawMessage " + e.getRawMessage());
                Log.i(TAG, "ErrorCode " + e.getErrorCode());
                Log.i(TAG, "StatusCode " + "status code = " + e.getStatusCode());
                return SERVER_ERROR;
            } catch (IOException e) {
                Log.e(TAG, e.toString());
                return CLIENT_ERROR;
            } finally {
                if (result != null) {
                    final long clientCrc = result.getClientCRC();
                    final long serverCrc = result.getServerCRC();
                    Log.i(TAG, objectKey + " content-length : " + result.getContentLength() + " clientCrc = " + clientCrc + " serverCrc = " + serverCrc);
                    Log.i(TAG, "content-length : " + result.getMetadata().getContentLength() + " clientCrc = " + clientCrc + " serverCrc = " + serverCrc);
                }
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        Log.e(TAG, e.toString());
                        return CLIENT_ERROR;
                    }
                }
            }
            return SUCCESS;
        }
        return CLIENT_ERROR;
    }

    /**
     * 直接获取流对象
     *
     * @param objectKey
     * @param <T>
     * @return
     */
    public <T> List<T> streamDownloadObjectList(final String objectKey, Class<T[]> cls) {
        ObjectListStreamDownloadCallback<T> callback = new ObjectListStreamDownloadCallback<T>(cls);
        streamDownload(objectKey, callback);
        return callback.getObject();
    }

    public <T> T streamDownloadObject(final String objectKey, Class<T> cls) {
        ObjectStreamDownloadCallback<T> callback = new ObjectStreamDownloadCallback<>(cls);
        streamDownload(objectKey, callback);
        return callback.getObject();
    }

    /**
     * 文件下载。直接保存在本地
     *
     * @param objectKey OSS服务器上要下载的文件路径
     * @param path      保存在本地的路径，如果是路径是文件夹，则文件名默认是下载文件的文件名
     * @return
     */
    public int fileDownload(final String objectKey, final String path) {
        if (mOSS != null) {
            Log.i(TAG, "threadId = " + Thread.currentThread().getId() + " file download objectKey " + objectKey);
            //检查是文件路径还是文件夹路径
            StringBuffer sb = new StringBuffer(path);
            File file = new File(path);
            if (file.isDirectory()) {
                sb.append(File.separator + objectKey.substring(objectKey.lastIndexOf("/") + 1));
            }
            Log.i(TAG, "local file path --> " + sb.toString());
            return streamDownload(objectKey, new FileStreamDownloadCallback(sb.toString()));

        }

        return CLIENT_ERROR;
    }

    private OSS createOSS(Context context) {
        if (mConfig != null) {
            //初始化
            Log.i(TAG, "createOSS..... ");
            //推荐使用OSSAuthCredentialsProvider。token过期可以及时更新
            OSSCredentialProvider credentialProvider = new OSSAuthCredentialsProvider(mConfig.getUrl());
            //该配置类如果不设置，会有默认配置，具体可看该类
            ClientConfiguration conf = new ClientConfiguration();
            conf.setConnectionTimeout(15 * 1000); // 连接超时，默认15秒
            conf.setSocketTimeout(15 * 1000); // socket超时，默认15秒
            conf.setMaxConcurrentRequest(5); // 最大并发请求数，默认5个
            conf.setMaxErrorRetry(2); // 失败后最大重试次数，默认2次

            return new OSSClient(context.getApplicationContext(), mConfig.getEndpoint(), credentialProvider);
        } else {
            Log.i(TAG, "please to set oss config...");
            return null;
        }
    }

}
